iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
自我挑戰組

程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript系列 第 6

Day 06|HTML 寫進 JavaScript?第一次理解 JSX

  • 分享至 

  • xImage
  •  

什麼是JSX?

對多數人來說,JSX 應該已經是熟透透的老朋友了吧!但對我而言又是一個全新的學習區塊,就跟認識一位新朋友一樣,我也準備來慢慢熟悉 JSX 啦~
在上一篇認識 React Component 時,我知道 Component 可以透過 return 回傳要顯示的畫面:

// App.tsx
function App() {
  return (
    <div className="app">
      <h1>Todo List</h1>
      <TodoForm onAdd={addTodo} />
      <TodoList
        todos={todos}
        onToggle={toggleTodo}
        onDelete={deleteTodo}
      />
    </div>
  )
}

但每次看到這種寫法,還是忍不住覺得真的蠻酷的。

為什麼 HTML 可以直接寫在 JavaScript 裡?
原來 return 後面這段長得很像 HTML 的語法稱為 JSX。
JSX 常被稱為 JavaScript XML,它是 JavaScript 的語法擴充,讓我們可以使用類似 HTML 的方式描述使用者介面的外觀。
雖然它長得很像 HTML,裡面卻又能直接使用 JavaScript,第一眼確實讓人覺得有點神奇。不過仔細拆開來看,好像又蠻直觀的。

JSX 不是真的 HTML

JSX 的寫法雖然和 HTML 很相似,但它並不是真的 HTML,也不能直接被瀏覽器理解。
在 React 專案執行與打包的過程中,Vite 等工具會先將 JSX 轉換成一般的 JavaScript,再交給 React 建立畫面。

例如:

const title = <h1>Todo List</h1>

這裡的 <h1>Todo List</h1> 並不是一段 HTML 字串,而是用 JSX 描述一個標題元素。
JSX 經過轉換後會成為 JavaScript 可以處理的值,因此它不只可以寫在 return 裡,也可以指定給變數或從函式中回傳。

function App() {
  return <h1>Todo List</h1>
}

這也讓我更理解上一篇提到的概念:React Component 是一個負責描述畫面的 function,而 JSX 就是它用來描述畫面的語法。

使用大括號進入 JavaScript

上一篇曾經留下一個問題:JSX 裡的 {} 代表什麼?
在 JSX 中,大括號 {} 就像是進入 JavaScript 的入口,可以用來放入變數、物件屬性或其他 JavaScript 運算式。
先來看一個簡單的例子:

const title = 'Todo List'

function App() {
  return <h1>{title}</h1>
}

這裡的:

{title}

會讀取 title 變數的內容,因此畫面上最後會顯示:

Todo List

在目前的 Todo List 專案中也能看到相同的寫法:

<p className="summary">
  已完成 {completedCount} / {todos.length} 項
</p>

這段 JSX 同時包含了固定文字與 JavaScript 的值:

  • 已完成是固定文字
  • {completedCount}是已完成的數量
  • {todos.length}是全部待辦事項的數量

假設目前共有 3 筆待辦事項,其中 1 筆已完成,畫面就會顯示:

已完成 1 / 3 項

因此,JSX 不只能顯示固定的文字,也可以將 JavaScript 的資料放進畫面。
大括號裡可以放的是 JavaScript「運算式」,也就是能夠產生一個結果的程式碼,例如:

{name}
{todos.length}
{completedCount + 1}

至於專案中比較複雜的 {todos.map(...)} 和條件判斷,會分別留到後面的清單渲染與條件渲染再繼續了解。

JSX 與 HTML 有哪些不同?

JSX 雖然看起來像 HTML,但還是有一些不同的語法規則。

1. class 要寫成 className

在 HTML 中,我們會使用 class 指定 CSS class:

<div class="app"></div>

但是在 JSX 中要改成 className

<div className="app"></div>

在目前的 App.tsx 中也可以看到:

<div className="app">
  <h1>Todo List</h1>
</div>

這一點上一篇介紹 React 的 CSS 時已經稍微提過,現在才知道它也是 JSX 與 HTML 的語法差異之一。
除了 className 之外,JSX 中還有 onClickonChangeonSubmit 這類駝峰式命名的事件屬性。
不過,它們不只是換了一種名稱,還可以接收 JavaScript 函式來處理事件。至於 React 的事件處理和 Vue 的 @click 有什麼差異,就留到後面再仔細研究研究。

2. 所有標籤都必須正確關閉

JSX 對標籤的語法要求比較嚴格,每一個標籤都必須正確關閉。
有內容的標籤需要同時寫出開始與結束標籤:

<button type="submit">新增</button>

沒有子內容的標籤則可以使用 /> 自我關閉:

<input type="text" />

目前專案中的輸入框也是使用自我關閉的寫法:

<input
  type="text"
  value={text}
  onChange={(event) => setText(event.target.value)}
  placeholder="輸入待辦事項..."
/>

自訂 Component 同樣需要正確關閉:

<TodoForm onAdd={addTodo} />

如果標籤沒有正確關閉,程式就會出現語法錯誤。

3. JSX 必須有一個共同的最外層元素

一個 Component 不能直接回傳多個並列的 JSX 元素。
例如下面的寫法會發生錯誤:

return (
  <h1>Todo List</h1>
  <TodoForm onAdd={addTodo} />
)

因為 <h1><TodoForm /> 的外面沒有共同的元素。
目前的 App.tsx 使用 <div> 將所有內容包起來:

return (
  <div className="app">
    <h1>Todo List</h1>
    <TodoForm onAdd={addTodo} />
    <TodoList
      todos={todos}
      onToggle={toggleTodo}
      onDelete={deleteTodo}
    />
  </div>
)

這樣 Component 回傳的內容就只有一個最外層元素。
如果不想為了包住內容而增加一個 <div>,也可以使用 React Fragment:

return (
  <>
    <h1>Todo List</h1>
    <TodoForm onAdd={addTodo} />
  </>
)

Fragment 的寫法是:

<>
  ...
</>

它可以將多個 JSX 元素包在一起,但不會在最後的 HTML 中增加一層多餘的元素。

JSX 裡的原生元素與自訂 Component

上一篇已經知道,React Component 的名稱必須以大寫字母開頭。
現在從 JSX 的角度再看一次,React 會根據標籤名稱的大小寫,判斷它代表的是 HTML 元素還是自訂 Component。
小寫開頭會被視為原生 HTML 元素:

<div></div>
<h1></h1>
<input />
<button></button>

大寫開頭則會被視為自訂 Component:

<App />
<TodoForm />
<TodoList />
<TodoItem />

例如 App.tsx 中的這段 JSX:

<div className="app">
  <h1>Todo List</h1>
  <TodoForm onAdd={addTodo} />
</div>

其中:

  • <div><h1>是原生 HTML 元素
  • <TodoForm />是自己建立的 React Component

這部分和上一篇認識 Component 的內容相同,所以這裡就先不重複深入,只要記得 JSX 可以同時組合原生元素與自訂 Component 就可以了。

JSX 裡的註解

一般 JavaScript 的單行註解可以寫成:

//這是 JavaScript 註解

但是在 JSX 標籤裡,註解的寫法不太一樣。
目前的 TodoList.tsx 中就有一段 JSX 註解:

<ul className="todo-list">
  {/* 用 map 把每一筆 todo 資料轉成一個 TodoItem 元件 */}
  {todos.map((todo) => (
    <TodoItem
      key={todo.id}
      todo={todo}
      onToggle={onToggle}
      onDelete={onDelete}
    />
  ))}
</ul>

JSX 裡的註解要寫成:

{/* 這是 JSX 裡的註解 */}

可以把它拆成兩個部分理解:

  1. 先使用 {} 進入 JavaScript
  2. 再使用 /* */ 寫區塊註解

今日回顧

今天先從最基本的部分認識了 JSX:

  • JSX 是 JavaScript 的語法擴充
  • JSX 看起來像 HTML,但不是真正的 HTML
  • JSX 會經過工具轉換成 JavaScript
  • Component 可以透過 return 回傳 JSX
  • 可以使用 {} 將 JavaScript 運算式放進 JSX
  • JSX 使用 className,而不是 HTML 的 class
  • JSX 的所有標籤都必須正確關閉
  • Component 回傳的 JSX 必須有共同的最外層元素
  • 可以使用 Fragment 避免產生多餘的 HTML 元素
  • JSX 可以同時使用原生 HTML 元素與自訂 Component
  • JSX 裡的註解要寫成 {/* ... */}

一開始看到 JSX 時,我的第一個印象是:「這看起來就像是 HTML + JavaScript 的混合體。」
實際從 Todo List 專案的程式碼慢慢拆解後,才發現 JSX 並不是單純地把 HTML 搬進 JavaScript,而是讓我們能用接近 HTML 的方式描述畫面,同時又能透過 {} 使用 JavaScript 的資料。
可以先把 JSX 想成 JavaScript 與使用者介面之間的一座橋:一邊使用 JavaScript 處理資料,一邊使用類似 HTML 的結構描述畫面這樣子的概念。

今天就先到這!
下一篇 Day 07,深入探索 React 的 Props!


上一篇
Day 05|.vue 不見了?React Component 原來長這樣
系列文
程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言